package com.cardone.platform.usercenter.service;

import com.cardone.common.annotation.RunAction;
import com.cardone.common.dto.PaginationDto;
import com.cardone.common.util.TextEncryptorUtils;
import com.cardone.context.CodeException;
import com.cardone.context.ContextHolder;
import com.cardone.platform.configuration.util.DictionaryUtils;
import com.cardone.platform.usercenter.dao.UserDao;
import com.cardone.platform.usercenter.dto.UserDto;
import com.cardone.platform.usercenter.util.UserUtils;
import com.cardone.security.token.dto.TokenDto;
import com.cardone.security.token.util.TokenUtils;
import com.google.common.collect.Maps;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.springframework.transaction.annotation.Transactional;

import java.util.Map;

/**
 * 用户服务
 *
 * @author yaohaitao
 */
@Getter
@Setter
@Transactional(readOnly = true)
public class UserDefaultService extends com.cardone.common.service.SimpleDefaultService<com.cardone.platform.usercenter.dto.UserDto> implements UserService {
    @Setter
    private static int saltSize = 4;

    @Override
    public UserDao getDao() {
        return ContextHolder.getBean(UserDao.class);
    }

    @Override
    @Transactional(readOnly = true)
    public Integer readByCodeNqIdForCount(final UserDto readUser) {
        return ContextHolder.getBean(UserDao.class).readByCodeNqIdForCount(readUser);
    }

    @Override
    @Transactional
    public UserDto saveByCode(final UserDto saveUser) {
        if (StringUtils.isBlank(saveUser.getCode())) {
            throw new CodeException("用户名不能为空值");
        }

        if (StringUtils.isBlank(saveUser.getPassword())) {
            throw new CodeException("密码不能为空值");
        }

        if (StringUtils.isBlank(saveUser.getFlagId())) {
            String flagId = DictionaryUtils.readIdByTypeCodeAndCode("user_type", "authorization");

            saveUser.setFlagId(flagId);

            saveUser.setFlagCode("authorization");
        }

        if (!SecurityUtils.getSubject().hasRole("admin")) {
            saveUser.setOrgId(UserUtils.find().getOrgId());
        }

        String salt = RandomStringUtils.randomNumeric(saltSize);

        final String encryptPassword = TextEncryptorUtils.encrypt(salt, saveUser.getPassword());

        saveUser.setPassword(encryptPassword);
        saveUser.setSalt(salt);

        if (StringUtils.isBlank(saveUser.getDataStateId())) {
            saveUser.setDataStateId(DictionaryUtils.readIdByTypeCodeAndCode("DATA_STATE_TYPE", "activity"));
        }

        if (StringUtils.isBlank(saveUser.getSexId())) {
            saveUser.setSexId(DictionaryUtils.readIdByTypeCodeAndCode("sex", "10"));
        }

        int insertCount = ContextHolder.getBean(UserDao.class).insertByNotExistsCode(saveUser);

        if (insertCount < 1) {
            throw new CodeException("用户帐号已有，请修改用户帐号");
        }

        return ContextHolder.getBean(UserDao.class).findById(saveUser.getId());
    }

    @Override
    @RunAction(outputParameterBeanId = "output-user",
            beforeBeanId = "before-operate-log", beforeTypeCode = "log-type", beforeCode = "login-before",
            errorBeanId = "error-operate-log", errorTypeCode = "log-type", errorCode = "login-error",
            afterBeanId = "after-operate-log", afterTypeCode = "log-type", afterCode = "login-after"
    )
    public UserDto login(final UserDto loginUser) {
        if (StringUtils.isBlank(loginUser.getCode())) {
            throw new CodeException("登录名不能为空");
        }

        return ContextHolder.getBean(UserService.class).findByCodeForLogin(UserDto.class, loginUser);
    }


    @Override
    @Transactional
    @RunAction(outputParameterBeanId = "output-map-user",
            beforeBeanId = "before-operate-log", beforeTypeCode = "log-type", beforeCode = "login-before",
            errorBeanId = "error-operate-log", errorTypeCode = "log-type", errorCode = "login-error",
            afterBeanId = "after-operate-log", afterTypeCode = "log-type", afterCode = "login-after"
    )
    public Map<String, Object> loginByOldTokenForToken(String clientId, String oldToken) {
        TokenDto tokenDto = TokenUtils.generateForOldToken(clientId, oldToken);

        UserDto user = ContextHolder.getBean(UserService.class).findById(tokenDto.getId().toString());

        Map<String, Object> data = Maps.newHashMap();

        data.put("user", user);
        data.put("token", tokenDto.getKey());

        return data;
    }

    @Override
    @Transactional
    @RunAction(outputParameterBeanId = "output-map-user",
            beforeBeanId = "before-operate-log", beforeTypeCode = "log-type", beforeCode = "login-before",
            errorBeanId = "error-operate-log", errorTypeCode = "log-type", errorCode = "login-error",
            afterBeanId = "after-entity-log", afterTypeCode = "log-type", afterCode = "login-after"
    )
    public Map<String, Object> loginByUsernameForToken(String clientId, String userFlagCode, String username, String password) {
        UserDto findUser = new UserDto();

        findUser.setFlagCode(userFlagCode);
        findUser.setCode(username);

        UserDto user = ContextHolder.getBean(UserService.class).findByCodeForLogin(UserDto.class, findUser);

        if (user == null) {
            throw new CodeException("账号不存在或密码错误");
        }

        if ("2".equals(user.getValidCode())) {
            throw new CodeException("帐号锁定"); //帐号锁定
        }

        if (StringUtils.isNotBlank(user.getSalt())) {
            String oldDecryptPassword = TextEncryptorUtils.decrypt(user.getSalt(), user.getPassword());

            if (!StringUtils.equals(password, oldDecryptPassword)) {
                throw new CodeException("账号不存在或密码错误");
            }
        } else {
            String newEncryptPassword = DigestUtils.md5Hex(password);

            if (!StringUtils.equals(newEncryptPassword, user.getPassword())) {
                throw new CodeException("账号不存在或密码错误");
            }
        }

        Map<String, Object> data = Maps.newHashMap();

        data.put("user", user);
        data.put("token", TokenUtils.generate(clientId, user.getId()).getKey());

        return data;
    }

    @Override
    @Transactional
    @RunAction(outputParameterBeanId = "output-user",
            beforeBeanId = "before-operate-log", beforeTypeCode = "log-type", beforeCode = "register-before",
            errorBeanId = "error-operate-log", errorTypeCode = "log-type", errorCode = "register-error",
            afterBeanId = "after-entity-log", afterTypeCode = "log-type", afterCode = "register-after"
    )
    public UserDto register(final UserDto registerUser) {
        if (StringUtils.isBlank(registerUser.getFlagId())) {
            String flagId = DictionaryUtils.readIdByTypeCodeAndCode("user_type", "registration");

            registerUser.setFlagId(flagId);
            registerUser.setFlagCode("registration");
        }

        final UserDto loginUser = this.saveByCode(registerUser);

        return loginUser;
    }

    @Override
    @Transactional
    public int updateEndDateByIdForInvalid(final String id) {
        return ContextHolder.getBean(UserDao.class).updateEndDateByIdForInvalid(id);
    }

    @Override
    @Transactional(readOnly = true)
    public UserDto findById(final UserDto findUser) {
        return ContextHolder.getBean(UserService.class).findById(UserDto.class, findUser);
    }

    @Override
    @Transactional(readOnly = true)
    public UserDto findById(final String id) {
        if (StringUtils.isBlank(id)) {
            throw new CodeException("用户标识不能为空值");
        }

        return ContextHolder.getBean(UserDao.class).findById(id);
    }

    @Override
    @Transactional(readOnly = true)
    public <P> P findByCodeForLogin(final Class<P> mappedClass, final UserDto findUser) {
        return ContextHolder.getBean(UserDao.class).findByCodeForLogin(mappedClass, findUser);
    }

    @Override
    @Transactional(readOnly = true)
    public PaginationDto<Map<String, Object>> paginationByUserId(Map<String, Object> paginationMap) {
        if (SecurityUtils.getSubject().hasRole("admin")) {
            paginationMap.put("isAdmin", true);
        } else {
            paginationMap.put("isAdmin", false);
            paginationMap.put("orgId", UserUtils.find().getOrgId());
        }

        return ContextHolder.getBean(UserDao.class).paginationByUserId(paginationMap);
    }

    @Override
    @Transactional
    @RunAction(
            beforeBeanId = "before-operate-log", beforeTypeCode = "log-type", beforeCode = "user-update-password-before",
            errorBeanId = "error-operate-log", errorTypeCode = "log-type", errorCode = "user-update-password-error",
            afterBeanId = "after-operate-log", afterTypeCode = "log-type", afterCode = "user-update-password-after"
    )
    public int updatePasswordById(String id, final String newPassword) {
        if (StringUtils.isBlank(id)) {
            id = UserUtils.find().getId();
        }

        if (StringUtils.isBlank(newPassword)) {
            throw new CodeException("新密码不能为空值");
        }

        String salt = RandomStringUtils.randomNumeric(saltSize);

        final String encryptPassword = TextEncryptorUtils.encrypt(salt, newPassword);

        final int updateCount = ContextHolder.getBean(UserDao.class).updatePasswordById(id, encryptPassword, salt);

        if (updateCount < 1) {
            throw new CodeException("用户标识不正确");
        }

        return updateCount;
    }

    @Override
    @Transactional
    @RunAction(
            beforeBeanId = "before-operate-log", beforeTypeCode = "log-type", beforeCode = "user-update-password-before",
            errorBeanId = "error-operate-log", errorTypeCode = "log-type", errorCode = "user-update-password-error",
            afterBeanId = "after-operate-log", afterTypeCode = "log-type", afterCode = "user-update-password-after"
    )
    public int updatePasswordById(final String id, final String oldPassword, final String newPassword) {
        return this.updatePasswordById(id, oldPassword, newPassword, true);
    }

    @Override
    @Transactional
    @RunAction(
            beforeBeanId = "before-operate-log", beforeTypeCode = "log-type", beforeCode = "user-update-password-before",
            errorBeanId = "error-operate-log", errorTypeCode = "log-type", errorCode = "user-update-password-error",
            afterBeanId = "after-operate-log", afterTypeCode = "log-type", afterCode = "user-update-password-after"
    )
    public int updatePasswordById(String id, final String oldPassword, final String newPassword, final Boolean isDiges) {
        UserDto user;

        if (StringUtils.isBlank(id)) {
            user = UserUtils.findForNotCache();

            id = user.getId();
        } else {
            user = this.getDao().findById(id);
        }

        if (StringUtils.isBlank(user.getId())) {
            throw new CodeException("用户不存在");
        }

        if (StringUtils.isBlank(newPassword)) {
            throw new CodeException("新密码不能为空值");
        }

        if (StringUtils.isNotBlank(oldPassword)) {
            if (BooleanUtils.isTrue(isDiges)) {
                if (StringUtils.isNotBlank(user.getSalt())) {
                    String oldDecryptPassword = TextEncryptorUtils.decrypt(user.getSalt(), user.getPassword());

                    if (!StringUtils.equals(oldDecryptPassword, oldPassword)) {
                        throw new CodeException("旧密码不正确");
                    }
                } else {
                    String oldEncryptPassword = DigestUtils.md5Hex(oldPassword);

                    if (!StringUtils.equals(oldEncryptPassword, user.getPassword())) {
                        throw new CodeException("旧密码不正确");
                    }
                }
            } else {
                if (!StringUtils.equals(oldPassword, user.getPassword())) {
                    throw new CodeException("旧密码不正确");
                }
            }
        }

        String encryptPassword;

        String salt = null;

        if (BooleanUtils.isTrue(isDiges)) {
            salt = RandomStringUtils.randomNumeric(saltSize);

            encryptPassword = TextEncryptorUtils.encrypt(salt, newPassword);
        } else {
            encryptPassword = newPassword;
        }

        final int updateCount = ContextHolder.getBean(UserDao.class).updatePasswordById(id, encryptPassword, salt);

        return updateCount;
    }

    @Override
    @Transactional
    @RunAction(
            beforeBeanId = "before-operate-log", beforeTypeCode = "log-type", beforeCode = "user-update-base-before",
            errorBeanId = "error-operate-log", errorTypeCode = "log-type", errorCode = "user-update-base-error",
            afterBeanId = "after-operate-log", afterTypeCode = "log-type", afterCode = "user-update-base-after"
    )
    public int updateForBaseInfo(final UserDto updateUser, final Boolean updateMobilePhone) {
        if (StringUtils.isBlank(updateUser.getId())) {
            throw new CodeException("用户标识不能为空值");
        }

        return ContextHolder.getBean(UserDao.class).updateForBaseInfo(updateUser, updateMobilePhone);
    }

    @Override
    @Transactional
    @RunAction(
            beforeBeanId = "before-operate-log", beforeTypeCode = "log-type", beforeCode = "user-update-full-before",
            errorBeanId = "error-operate-log", errorTypeCode = "log-type", errorCode = "user-update-full-error",
            afterBeanId = "after-operate-log", afterTypeCode = "log-type", afterCode = "user-update-full-after"
    )
    public int updateForFullInfo(final UserDto updateUser, final Boolean updateMobilePhone) {
        if (StringUtils.isBlank(updateUser.getId())) {
            throw new CodeException("用户标识不能为空值");
        }

        return ContextHolder.getBean(UserDao.class).updateForFullInfo(updateUser, updateMobilePhone);
    }

    @Override
    @Transactional
    @RunAction(
            beforeBeanId = "before-operate-log", beforeTypeCode = "log-type", beforeCode = "user-update-detail-before",
            errorBeanId = "error-operate-log", errorTypeCode = "log-type", errorCode = "user-update-detail-error",
            afterBeanId = "after-operate-log", afterTypeCode = "log-type", afterCode = "user-update-detail-after"
    )
    public int updateForDetail(final UserDto updateUser) {
        if (StringUtils.isBlank(updateUser.getId())) {
            throw new CodeException("用户标识不能为空值");
        }

        if (StringUtils.isNotBlank(updateUser.getPassword())) {
            String salt = RandomStringUtils.randomNumeric(saltSize);

            final String encryptPassword = TextEncryptorUtils.encrypt(salt, updateUser.getPassword());

            updateUser.setPassword(encryptPassword);
            updateUser.setSalt(salt);
        }

        return ContextHolder.getBean(UserDao.class).updateForDetail(updateUser);
    }

    @Override
    @Transactional
    @RunAction(
            beforeBeanId = "before-operate-log", beforeTypeCode = "log-type", beforeCode = "user-update-mobile-phone-before",
            errorBeanId = "error-operate-log", errorTypeCode = "log-type", errorCode = "user-update-mobile-phone-error",
            afterBeanId = "after-operate-log", afterTypeCode = "log-type", afterCode = "user-update-mobile-phone-after"
    )
    public int updateMobilePhone(final String id, final String mobilePhone) {
        return ContextHolder.getBean(UserDao.class).updateMobilePhone(id, mobilePhone);
    }

    @Override
    @Transactional
    public int updateByCode(UserDto updateUser, String... updateNames) {
        if (!SecurityUtils.getSubject().hasRole("admin")) {
            updateUser.setOrgId(UserUtils.find().getOrgId());
        }

        return super.updateByCode(updateUser, updateNames);
    }

    @Override
    @Transactional
    public int updateByCode(UserDto updateUser, String[] whereAndEqProperties, String... updateNames) {
        if (!SecurityUtils.getSubject().hasRole("admin")) {
            updateUser.setOrgId(UserUtils.find().getOrgId());
        }

        return super.updateByCode(updateUser, whereAndEqProperties, updateNames);
    }
}
